package de.endrullis.idea.postfixtemplatesgenerator;

import cn.hutool.core.comparator.VersionComparator;
import cn.hutool.core.exceptions.DependencyException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.log.dialect.console.ConsoleLog;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author liuxiongkang
 */
public class DependencyCollector4Hutool {
    public static final String PROJECT_PARENT_VERSION = "${project.parent.version}";
    public static final String HUTOOL_VERSION_PATTERN = "${hutool.version}";
    public static final String DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
    public static final SAXReader XML_READER = initSaxReader();
    public static final String SYSTEM = "system";
    private static final ConsoleLog LOG = new ConsoleLog(DependencyCollector4Hutool.class);
    private static final File PRJ_POM = FileUtil.file(FileUtil.file(DependencyCollector4Hutool.class.getResource("/")).getParentFile().getParentFile(), "pom.xml");
    private static final String VERSION = "version";

    public static void main(String[] args) throws DocumentException, IOException {
        // 方式1 : 从运行环境 而不是源码 拿 pom, 需要自己在依赖中指定 hutool-xxx dependencies
        // runtimeUpdate(collectRuntimeDependency());
        // 方式2 : 从源码中 pom 拿dependencies, 只用pull最新源码就行
        runtimeUpdate(collectSourceDependency("D:\\itsoft\\workspace_idea_self\\00d-to-study\\hutool"));
    }

    /**
     * @param files hutool-xxx  pom.xml , 用于更新本项目 pom.xml
     */
    public static void runtimeUpdate(List<File> files) throws DocumentException, IOException {
        // 1  从 hutool-xxx.pom 收集  properties 和 dependencies , 去掉 scope, optional 标签
        Set<Element> property = new HashSet<>();
        Set<Element> dependency = new HashSet<>();
        collectorRuntimeInfo(files, property, dependency);
        // 2  找到当前项目 pom.xml , 更新 properties 和 dependencies
        updateProjectDependency(property, dependency);
        LOG.info("成功更新hutool项目最新依赖!");
    }

    /**
     * @return 从 本项目运行环境 找到 hutool-xxx  pom.xml
     */
    public static List<File> collectRuntimeDependency() {
        List<File> files = ResourceUtil.getResources("META-INF/maven").stream().filter(url -> url.getPath().contains("hutool-")).map(FileUtil::file).collect(Collectors.toList());
        files.add(PRJ_POM);
        return files;
    }

    /**
     * @param path D:\\xxxx\\workspace_idea_self\\hutool
     *
     * @return 从 hutool 源码根路径 找到 hutool-xxx pom.xml
     */
    public static List<File> collectSourceDependency(String path) {
        List<File> files = FileUtil.loopFiles(path, f -> f.isFile() && f.getPath().contains("hutool-") && f.getName().startsWith("pom.xml"));
        files.add(PRJ_POM);
        return files;
    }

    private static void collectorRuntimeInfo(List<File> files, Set<Element> property, Set<Element> dependency) throws DocumentException {
        Assert.notEmpty(files, "hutool pom.xml路径 为空!");
        // 3.1  收集   properties 和 dependencies
        for (File pomFile : files) {
            Element rootElement = XML_READER.read(pomFile).getRootElement();
            List<Element> properties = rootElement.elements("properties");
            if (!properties.isEmpty()) {
                property.addAll(properties.get(0).elements());
            }
            Element dependencies = rootElement.element("dependencies");
            if (null == dependencies) {
                LOG.warn(pomFile + " 找不到依赖包!");
                continue;
            }
            List<Element> dependencyNodeEle = dependencies.elements("dependency").stream().map(amendElement()).collect(Collectors.toList());
            dependency.addAll(dependencyNodeEle);
        }
        // 3.2 添加主版本号
        Element versionPom = XML_READER.read(files.get(0)).getRootElement();
        Assert.notNull(versionPom, "未找到hutool 版本号");
        if (null != versionPom) {
            String version = versionPom.element("parent").elementText(VERSION);
            Element versionElement = DocumentHelper.createElement("hutool.version");
            versionElement.setText(version);
            property.add(versionElement);
        }
    }

    private static void updateProjectDependency(Set<Element> property, Set<Element> dependency) throws DocumentException, IOException {
        Document prjDocument = XML_READER.read(PRJ_POM);
        Element prjRootElement = prjDocument.getRootElement();
        // 1 合并 去重   取同一依赖最大版本
        Map<String, List<Element>> propertyPart = property.stream().collect(Collectors.groupingBy(e -> e.getQName().getName()));
        property = propertyPart.values().stream().map(l -> l.stream().max(Comparator.comparing(Element::getTextTrim, VersionComparator.INSTANCE)).orElse(l.get(0))).collect(Collectors.toSet());
        // 优先取version 带 ${} 的,方便版本控制,
        Map<String, List<Element>> dependencyPart = dependency.stream().collect(Collectors.groupingBy(e -> e.elementTextTrim("groupId") + ":" + e.elementTextTrim("artifactId")));
        dependency = dependencyPart.values().stream().map(l -> l.stream().filter(e -> e.elementTextTrim(VERSION).contains("${")).findFirst().orElse(
                // 取同一依赖最大版本
                l.stream().max(Comparator.comparing(e -> e.elementTextTrim(VERSION), VersionComparator.INSTANCE)).orElse(l.get(0)))).collect(Collectors.toSet());
        // 2 添加 dependencies
        Element prjDependenciesElement = prjRootElement.element("dependencies");
        prjDependenciesElement.clearContent();
        dependency.forEach(element -> prjDependenciesElement.add((Element) element.clone()));
        // 添加 properties
        Element prjPropertiesElement = prjRootElement.element("properties");
        prjPropertiesElement.clearContent();
        property.forEach(element -> prjPropertiesElement.add((Element) element.clone()));
        // 同步到pom.xml文件
        // LOG.debug("更新后pom.xml为 \r\n {}", prjRootElement.asXML())
        write(prjDocument);
    }

    private static void write(Document prjDocument) throws IOException {
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setIndent(true);
        format.setNewlines(true);
        format.setEncoding("utf-8");
        XMLWriter writer = new XMLWriter(new FileWriter(PRJ_POM), format);
        writer.write(prjDocument);
        writer.close();
    }

    private static Function<Element, Element> amendElement() {
        return e -> {
            Element scope = e.element("scope");
            if (null != scope) {
                if (SYSTEM.equalsIgnoreCase(scope.getTextTrim())) {
                    LOG.warn(" {} 含 属性scope={} ", e.getStringValue(), SYSTEM);
                } else {
                    e.remove(scope);
                }
            }
            Element optional = e.element("optional");
            if (null != optional) {
                e.remove(optional);
            }
            String version = e.elementText(VERSION);
            if (PROJECT_PARENT_VERSION.equals(version)) {
                e.element(VERSION).setText(HUTOOL_VERSION_PATTERN);
            }
            return e;
        };
    }

    private static SAXReader initSaxReader() {
        try {
            SAXReader xmlReader = new SAXReader();
            xmlReader.setFeature(DOCTYPE_DECL, true);
            return xmlReader;
        } catch (SAXException e) {
            throw new DependencyException(e);
        }
    }
}